﻿using System;
using System.Linq.Expressions;

namespace Kyoh
{
	using Binary = Func<ParameterExpression, ParameterExpression, BinaryExpression>;
	using Unary = Func<ParameterExpression, UnaryExpression>;

	/// <summary>
	/// 動的にジェネリック型 T の加減乗除関数を作る。
	/// </summary>
	/// <typeparam name="T">対象となる型。</typeparam>
	public static class Operator<T>
	{
		static readonly ParameterExpression x = Expression.Parameter(typeof(T), "x");
		static readonly ParameterExpression y = Expression.Parameter(typeof(T), "y");
		static readonly ParameterExpression z = Expression.Parameter(typeof(T), "z");
		static readonly ParameterExpression w = Expression.Parameter(typeof(T), "w");

		public static readonly Func<T, T, T> Add = Lambda(Expression.Add);
		public static readonly Func<T, T, T> Subtract = Lambda(Expression.Subtract);
		public static readonly Func<T, T, T> Multiply = Lambda(Expression.Multiply);
		public static readonly Func<T, T, T> Divide = Lambda(Expression.Divide);
		public static readonly Func<T, T> Plus = Lambda(Expression.UnaryPlus);
		public static readonly Func<T, T> Negate = Lambda(Expression.Negate);
		public static readonly Func<T, T, bool> Equal = Equality(Expression.Equal);
		public static readonly Func<T, T, bool> NotEqual = Equality(Expression.NotEqual);
		public static readonly Func<T, T, bool> LessThan = Equality(Expression.LessThan);
		public static readonly Func<T, T, bool> LessThanOrEqual = Equality(Expression.LessThanOrEqual);
		public static readonly Func<T, T, bool> GreaterThan = Equality(Expression.GreaterThan);
		public static readonly Func<T, T, bool> GreaterThanOrEqual = Equality(Expression.GreaterThanOrEqual);

		public static T Convert<TInput>(TInput value)
		{
			var lambda = Expression.Lambda<Func<TInput, T>>(Expression.Convert(Expression.Parameter(typeof(TInput), "obj"), typeof(T))).Compile();
			return lambda(value);
		}

		public static TOutput Convert<TOutput>(T value)
		{
			var lambda = Expression.Lambda<Func<T, TOutput>>(Expression.Convert(Expression.Parameter(typeof(T), "obj"), typeof(TOutput))).Compile();
			return lambda(value);
		}

		public static T Absolute(T value)
		{
			return LessThan(value, default(T))
					? Negate(value)
					: value;
		}

		public static bool IsNotZero(T value)
		{
			return NotEqual(value, default(T));
		}

		public static bool IsZero(T value)
		{
			return Equal(value, default(T));
		}

		public static bool IsPositive(T value)
		{
			return GreaterThan(value, default(T));
		}

		public static bool IsNotNegative(T value)
		{
			return GreaterThanOrEqual(value, default(T));
		}

		public static bool IsNegative(T value)
		{
			return LessThan(value, default(T));
		}

		public static bool IsNotPositive(T value)
		{
			return LessThanOrEqual(value, default(T));
		}

		public static Func<T, T, bool> Equality(Binary op)
		{
			return Expression.Lambda<Func<T, T, bool>>(op(x, y), x, y).Compile();
		}

		public static Func<T, T, T> Lambda(Binary op)
		{
			return Expression.Lambda<Func<T, T, T>>(op(x, y), x, y).Compile();
		}

		public static Func<T, T> Lambda(Unary op)
		{
			return Expression.Lambda<Func<T, T>>(op(x), x).Compile();
		}

		public static readonly Func<T, T, T, T, T> ProductSum =
			Expression.Lambda<Func<T, T, T, T, T>>(
				Expression.Add(
					Expression.Multiply(x, y),
					Expression.Multiply(z, w)),
				x, y, z, w).Compile();

		public static readonly Func<T, T, T, T, T> ProductDifference =
			Expression.Lambda<Func<T, T, T, T, T>>(
				Expression.Subtract(
					Expression.Multiply(x, y),
					Expression.Multiply(z, w)),
				x, y, z, w).Compile();


	}
}
